#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <parse.h>
#include <externs.h>
#include <init.h>
#include <regular_expression.h>
#include <sys/types.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include <linux/limits.h>
#include <fcntl.h>
#include <strings.h>
#include <signal.h>
#include <execute.h>
#include <builtin.h>

/*
*一个shell解析的主要循环
*/
void
shell_loop(void)
{
	while(1) {
		init();//每次读取命令之前都要进行一次初始化
		//DEBUG_PRINT("read_command\n");
		if(-1 == read_command()) {
			perror("read_command");
			break;
		}
	#ifdef	USE_REG_EXP
		parse_command();
	#else
		lesson_parse_command();
	#endif
	#ifdef _DEBUG
		print_command();
	#endif
		execute_command();
	}
	printf("\n exit \n");
}
/*
*读取命令：从控制台接受命令，然后传递给解析命令
*/
int
read_command(void)
{
	//get input
	//这里该函数被信号中断后，会导致不能执行信号处理函数而退出程序的情况
	//分析原因，可能因为这个fgets是不可重入的函数（在执行过程中被信号打断后
	//继续执行会产生不可预期结果）
	#if 0
	if(NULL == fgets(cmdline,MAXLINE,stdin)) {
			return -1;
	}
	#else
	int ret;
	do {
		ret = read(STDIN_FILENO,cmdline,MAXLINE);
	} while(-1 == ret && EINTR == errno);
	if(ret > 0)
		DEBUG_PRINT("read cmd:%s",cmdline);
	else
		return ret;
	#endif
	return 0;
}
static void get_command(int i);
static void getname(char *name);
#ifdef USE_REG_EXP
/*
*解析命令：对命令进行进一步的解析，解析为执行命令所需要的格式
*/
int
parse_command(void)
{
	if(check("^\n"))
		return 0;
	//将字符串命令处理成标准形式
	/*parse str "ls -l" to "ls\0-l\0"*/
	/*how to parse "cat < text.txt | grep -n public > test2.txt &"*/
	char *cp = cmdline;
	//然后这里去掉多余的空格
	DEBUG_PRINT("before:%s",cp);
	regular_expression(cp,"s/\\s{2,100}/\\ /");
	DEBUG_PRINT("after:%s",cp);
	//首先判断是否是shell内部命令
	if(builtin()) {
		//如果是内部命令就执行它，然后返回为真后
		//后面的解析就不必要了。
		return 0;
	}
	//解析第一条简单命令
	get_command(0);
	//判定是否有输入重定向符
	if(check("<"))
		getname(infile);
	//判定是否有管道
	int i;
	for(i = 1;i < PIPELINE;++i) {
		if(check("\\|")) {
			get_command(i);
		} else {
			break;
		}
	}
	//判定是否有输出重定向符
	//目前来看，这个还有一些问题
	if(check(">")) {
		if(check(">{2}"))
			append = 1;//说明是以追加的方式输出到文件
		getname(outfile);
	}
	//判定是否有后台作业
	if(check("&")) {
		backgnd = 1;
	} 
	//判定一条命令结束'\n'
	if(check("\n")) {
		cmd_count = i;
		return cmd_count;
	} else {
		fprintf(stderr,"Command line syntax error\n");
		return -1;
	}
	return 0;
}
#endif

int
lesson_parse_command(void)
{
	/*how to parse "cat < text.txt | grep -n public > test2.txt &"*/
	if(check("\n"))
		return 0;

	//这里还要区分是shell的内部命令或者外部命令
	if(builtin()) {
		//如果是内部命令就执行它，然后返回为真后
		//后面的解析就不必要了。
		return 0;
	}

	//解析第一条简单命令
	get_command(0);
	//判定是否有输入重定向符
	if(check("<"))
		getname(infile);
	//判定是否有管道
	int i;
	for(i = 1;i < PIPELINE;++i) {
		if(check("|")) {
			get_command(i);
		} else {
			break;
		}
	}
	//判定是否有输出重定向符
	if(check(">")) {
		if(check(">"))
			append = 1;//说明是以追加的方式输出到文件
		getname(outfile);
	}
	//判定是否有后台作业
	if(check("&")) {
		backgnd = 1;
	} 
	//判定一条命令结束'\n'
	if(check("\n")) {
		cmd_count = i;
		return cmd_count;
	} else {
		fprintf(stderr,"Command line syntax error\n");
		return -1;
	}
}
/*
*运行命令：对命令进行执行
*/
int
execute_command(void)
{
	execute_disk_command();
	return 0;
}

void print_command(void)
{
	int i;
	int j;
	printf("cmd_count = %d\n",cmd_count);
	if('\0' != infile[0])
		printf("infile = [%s]\n",infile);
	if('\0' != outfile[0])
		printf("outfile = [%s]\n",outfile);
	for(i = 0;i < cmd_count;++i) {
		j = 0;
		while(NULL != cmd[i].args[j]) {
			printf("[%s] ",cmd[i].args[j]);
			j++;
		}
		printf("\n"); 
	}
}

/*
* 解析简单命令到cmd[i]当中
* 提取命令参数到avline当中
* 并且将COMMAND结构中的args
* 中的每个指针指向解析出来的参数
*/
void 
get_command(int i)
{
	int j = 0,inword = 0;
	while('\0' != *lineptr) {
		#ifdef	USE_REG_EXP
		while(' ' == *lineptr || '\t' == *lineptr \
				|| ('|' == *lineptr && 0 != i)) {
			lineptr++;
		}
		#else
		//去掉多余的左空格
		while(' ' == *lineptr ||
			 '\t' == *lineptr) {
			lineptr++;
		}
		#endif
		cmd[i].args[j] = avptr;

		//将cmdline中的命令参数提取到avline中
		while('\0' != *lineptr &&
				' ' != *lineptr && 
				'\t' != *lineptr &&
				'>' != *lineptr &&
				'<' != *lineptr &&
				'|' != *lineptr &&
				'&' != *lineptr &&
				'\n' != *lineptr)
		{
			*avptr++ = *lineptr++;
			inword = 1;//表明正在解析命令字符
		}
		*avptr++ = '\0';
		DEBUG_PRINT("arg %s \n",cmd[i].args[j]);
		switch(*lineptr)
		{
		//说明解析到了一个参数
		case ' ':
		case '\t':
			j++;
			inword = 0;//重新开始进行命令解析
			break;
		//说明命令解析结束了
		case '<':
		case '>':
		case '|':
		case '&':
		case '\n':
			if(0 == inword) {
				//说明没有解析到命令，所以
				//也没有必要将这个作为参数
				cmd[i].args[j] = NULL;
			}
			return;
		default: //for '\0'
			return;
		}
	}
}

//将lineptr中的字符串与str进行匹配
//成功返回1lineptr移动过所匹配的字符串
//失败返回0 lineptr保持不变
int 
check(const char *str)
{
	//去掉空格
#ifdef	USE_REG_EXP
	if(' ' == *lineptr || '\t' == *lineptr) {
		//过滤开头的空格使用正则表达式的方式
		regular_expression(lineptr,"s/^\\s+//");
	}
	//使用正则的方式进行匹配
	int match_reg_str_len = strlen(str) + 5;
	char *match_reg_str = (char *)malloc(match_reg_str_len);
	bzero(match_reg_str,match_reg_str_len);
	sprintf(match_reg_str,"m/%s/",str);
	if(regular_expression(lineptr,match_reg_str)) {
		return 1;
	}
	return 0;
#else
	char *p;
	while(' ' == *lineptr || '\t' == *lineptr)
		lineptr++;
	//使用简单的办法对字符串进行匹配
	p = lineptr;
	while('\0' != *str && *p == *str) {
		str++;
		p++;
	}

	if('\0' == *str) {
		lineptr = p;//lineptr移过所匹配的字符串
		return 1;
	}
	//lineptr保持不变
	return 0;
#endif	
}

//获取文件名字
void 
getname(char *name)
{
	//去掉空格
	#ifdef	USE_REG_EXP
	while(' ' == *lineptr || '\t' == *lineptr || \
			'<' == *lineptr || '>' == *lineptr)
		lineptr++;
	#else
	while(' ' == *lineptr || '\t' == *lineptr)
		lineptr++;
	#endif
	while('\0' != *lineptr &&
				' ' != *lineptr && 
				'\t' != *lineptr &&
				'>' != *lineptr &&
				'<' != *lineptr &&
				'|' != *lineptr &&
				'&' != *lineptr &&
				'\n' != *lineptr) {
		*name++ = *lineptr++;
	}
	*name = '\0';
}
